/*
 * TuplesProcessing.cpp
 *
 *  Created on: 31/01/2014
 *      Author: root
 */

#include "TuplesProcessing.h"

using namespace std;

extern DppmParams * dppm;
extern Tuple * bestSolution;
extern Tuple * currentTuple;
extern int * lastTupleModes;
extern int * minCosts;
extern int * minTimes;

bool isGreater(Tuple * t1, int pos, int mode, int * t2);
int evalSolution(Tuple * candidate);
int evalSolution(int * candidate);


int TuplesProcessing(){

//	int debug = 1;
//	while(debug);

	int act = -1, mode, maxTimePreds, n = dppm->getNumActivities(), numPreds;
	int partialCost = 0, deadline = dppm->getDeadline(), i;
	int * preds;
	bool shiftBranch;
	int bsChange = 0;
	MPI_Status status;
	int msg = 0;
	int tag = 0;
	int flag = 0;
	int buff[Tuple::tuplesIntSize];
	Tuple * newBestSolution;
	int iter = 0;

	//Encontrar el primer prefijo factible
	do{

		act++;
		partialCost += dppm->getActivityModeCost(act,currentTuple->modes(act));

	} while((act <= n - 2) &&
			(currentTuple->partialTimes(act) + minTimes[act] < deadline) &&
			(partialCost + minCosts[act] < bestSolution->cost()));

	mode = currentTuple->modes(act);
	currentTuple->cost(partialCost - dppm->getActivityModeCost(act, mode));

	while (!isGreater(currentTuple, act, mode, lastTupleModes)){

		iter++;

//		printf("act=%d, mode=%d\n",act,mode);
//		currentTuple->print();
//		printf("-----------------------------------------\n\n");

		//Predecesores la siguiente actividad
		numPreds = dppm->getActivityNumPreds(act);
		preds = dppm->getActivityPreds(act);

		//Calculo el tiempo maximo que demoran sus predecesores
		maxTimePreds = 0;
		for (i = 0; i < numPreds; i++){
			if( currentTuple->times(preds[i]) > maxTimePreds ){
				maxTimePreds = currentTuple->times(preds[i]);
			}
		}


		//BOUNDING

		//Me salteo todos los modos con los cuales me paso del deadline tomando los tiempos mas bajos
		while((mode < dppm->getActivityNumModes(act))
			       && (maxTimePreds + dppm->getActivityModeTime(act, mode) + minTimes[act] > deadline)) {

			mode++;

		};

		if ( ! (shiftBranch = (mode == dppm->getActivityNumModes(act))) ){

			if ( currentTuple->cost() + dppm->getActivityModeCost(act,mode) + minCosts[act] >= bestSolution->cost()){

				mode = dppm->getActivityNumModes(act);
				shiftBranch = true;

			}else{
				//Setear (act,modo)
				currentTuple->modes(act, mode);
				currentTuple->times(act, maxTimePreds + dppm->getActivityModeTime(act, mode) );
				if ((act == 0) || (currentTuple->times(act) > currentTuple->partialTimes(act - 1))){
					currentTuple->partialTimes(act, currentTuple->times(act));
				}else{
					currentTuple->partialTimes(act, currentTuple->partialTimes(act - 1));
				}
				currentTuple->cost( currentTuple->cost() + dppm->getActivityModeCost(act, mode) );

				if (act < n - 1){

					mode = 0;
					act ++;

				}else{

					bsChange = evalSolution(currentTuple);
					currentTuple->cost( currentTuple->cost() - dppm->getActivityModeCost(act, mode++) );

					mode = bsChange ? dppm->getActivityNumModes(act) : mode + 1;

					shiftBranch = true;

				}

			}
		}

		if (shiftBranch){

			while ( (act > 0) && (mode == dppm->getActivityNumModes(act)) ){

				act--;

				currentTuple->cost( currentTuple->cost() -
						dppm->getActivityModeCost(act, currentTuple->modes(act)) );

				mode = currentTuple->modes(act) + 1;

			}

		}


		if(bsChange){
			bsChange = 0;
			MPI_Send(&msg,1,MPI_INT,MASTER_RANK, BEST_SOLUTION_UPDATE, MPI_COMM_WORLD);
			MPI_Send(bestSolution->getArray(),Tuple::tuplesIntSize,MPI_INT,0, BEST_SOLUTION_UPDATE, MPI_COMM_WORLD);
		}


		MPI_Iprobe(0, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);
		if(flag){
			MPI_Recv(&msg, 1, MPI_INT, status.MPI_SOURCE, status.MPI_TAG, MPI_COMM_WORLD, &status);
			switch( status.MPI_TAG ){

				case BEST_SOLUTION_UPDATE:
					MPI_Recv(buff, Tuple::tuplesIntSize, MPI_INT, status.MPI_SOURCE, status.MPI_TAG, MPI_COMM_WORLD, &status);
					evalSolution(buff);
					break;
			}
		}

	}

	MPI_Send(&msg,1,MPI_INT,0, PROCESS_END, MPI_COMM_WORLD);
	MPI_Send(bestSolution->getArray(),Tuple::tuplesIntSize,MPI_INT,0,PROCESS_END,MPI_COMM_WORLD);

	return iter;

}

bool isGreater(Tuple * t1, int pos, int mode, int * t2){

	for (int i = 0; i < pos; i++){

		if (t1->modes(i) != t2[i]){

			return t1->modes(i) > t2[i];

		}
	}

	return mode > t2[pos];
}


int evalSolution(Tuple * candidate){

	if (bestSolution->cost() <= candidate->cost()){
		return 0;
	}else{
		delete bestSolution;
		bestSolution = new Tuple(candidate);
		return 1;
	}
}


int evalSolution(int * candidate){

	if (bestSolution->cost() <= Tuple::GetCostFromTupleArray(candidate)){
		return 0;
	}else{
		delete bestSolution;
		bestSolution = new Tuple(Tuple::tuplesIntSize, candidate);
		return 1;
	}
}
